package com.fs.base.jpa.specification;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;

import com.fs.base.jpa.factory.Restrictions;
import com.fs.base.jpa.interf.QueryOpt;
import com.fs.base.jpa.vo.LogicalExpression;
import com.fs.base.utils.DateUtils;

/**
 * @author TiddlerCJ
 * @desc 定义一个查询条件容器 
 */
public class Criteria<T> implements Specification<T>{
	/**
	 * 查询连字符 3个下划线
	 */
	private final static String QUERY_SPLIT = "___";
	private List<QueryOpt> queryList = new ArrayList<QueryOpt>(); 

	public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
			CriteriaBuilder builder) {
		if (!queryList.isEmpty()) {
			List<Predicate> predicates = new ArrayList<Predicate>();
			for(QueryOpt c : queryList){
				predicates.add(c.toPredicate(root, query,builder));
			}
			// 将所有条件用 and 联合起来
			if (predicates.size() > 0) {
				return builder.and(predicates.toArray(new Predicate[predicates.size()]));
			}
		}
		return builder.conjunction();
	}
	
	/**
	 * 增加简单条件表达式
	 * @Methods Name add
	 * @Create In 2012-2-8 By lee
	 * @param expression0 void
	 */
	public void add(QueryOpt criterion){
		if(criterion!=null){
			queryList.add(criterion);
		}
	}

	/*public void or(QueryOpt... criterions){
		if(criterions!=null){
			add(Restrictions.or(criterions));
		}
	}*/
	
	@SuppressWarnings("unchecked")
	public Criteria<T> addAuto(String fieldName,Object value,String opt){
		if(StringUtils.isBlank(opt)) opt = "like";
		//System.out.println("value:"+ value);
		//System.out.println("fieldName:"+ fieldName);
		//if(value==null || value=="" || StringUtils.isBlank(fieldName)) return this;
		if(value==null  || StringUtils.isBlank(fieldName)) return this;
		if("like".equals(opt)){
			addLike(fieldName, value);
		}else if("eq".equals(opt)){
			addEq(fieldName, value);
		}else if("neq".equals(opt)){
			addNeq(fieldName, value);
		}else if("gt".equals(opt)){
			addGt(fieldName, value);
		}else if("gte".equals(opt)){
			addGte(fieldName, value);
		}else if("lt".equals(opt)){
			addlt(fieldName, value);
		}else if("lte".equals(opt)){
			addlte(fieldName, value);
		}else if("in".equals(opt)){
			List<Object> list = new ArrayList<Object>();
			if(value instanceof String[]){
				list = Arrays.asList((Object[])value);
			}else if(value instanceof Collection<?>){
				list = (List<Object>) value ;
			}else if(value instanceof String){
				Object [] v = String.valueOf(value).split(",");
				if(v!=null) {
					list = Arrays.asList(v);
				}
			}
			if(!CollectionUtils.isEmpty(list)){
				addIn(fieldName, list);
			} 
		}else if("between".equals(opt)){//在两个数之间
			Object[] values = (Object[]) value;
			int len = values.length;
			if(len>=1){
				addGte(fieldName, values[0]);
			}
			if(len>=2){
				addlte(fieldName, values[1]);
			}
		}else{
			//TODO...
		}
		return this;
	}
	
	/**
	 * 自动选择查询方式
	 * @param fieldName
	 * @param data
	 * @return
	 */
	public Criteria<T> addAuto(Map<String, Object> data){
		if(CollectionUtils.isEmpty(data)) return this;
		String format = null;
		for(String fieldName :data.keySet()){
			Object value = data.get(fieldName);
			if(fieldName.endsWith("[]")){
				fieldName = fieldName.replace("[]", "");
			}
			format = fieldName.concat("_format");
			if(data.containsKey(format)){
				format = data.get(format).toString();
			}else{
				format = null;
			}
			value = formatValue(value,format);
			
			Object opt = null;
			//eg: name=张三___eq
			if(value instanceof String && value.toString().indexOf(QUERY_SPLIT)>-1){
				String[] valueOpt = value.toString().split(QUERY_SPLIT);
				value = valueOpt[0];
				opt = valueOpt[1];
				addAuto(fieldName,value,String.valueOf(opt));
			}else{
				if(fieldName.indexOf(QueryOpt.QUERY_POSTFIX)>-1) continue;
				opt = data.get(fieldName + QueryOpt.QUERY_POSTFIX);
				
				if(opt!=null) {
					addAuto(fieldName,value,String.valueOf(opt));
				}
			}
		}
		return this;
	}
	
	private Object formatValue(Object value,String format){
		Object result = null;
		if(StringUtils.isNotBlank(format)){
			if(value instanceof String[]){
				String[] arrays = (String[]) value;
				Object [] objs = new Object[arrays.length];
				for(int i = 0; i < objs.length; i++){
					objs[i] = format(arrays[i], format);
				}
				result = objs;
			}else{
				result = format(value.toString(), format);
			}
		}else{
			result = value;
		}
		return result;
	}
	
	private Object format(String v,String format){
		Object value = null;
		if(StringUtils.isNotBlank(v) && StringUtils.isNotBlank(format)){
			try {
				if("dateTime".equals(format)){
					value = DateUtils.parseDate(v, "yyyy-MM-dd HH:mm:ss");
				}
				if("date".equals(format)){
					value = DateUtils.parseDate(v, "yyyy-MM-dd");
				}
				if("month".equals(format)){
					value = DateUtils.parseDate(v, "yyyy-MM");
				}
			} catch (Exception e) {
				System.err.println(v + "转换成"+ format +"格式出现异常！");
			}
		}else{
			value = v;
		}
		return value;
	}
	
	/**
	 * like查询
	 * @param param
	 * @param value
	 * @return
	 */
	public Criteria<T> addLike(String fieldName,Object value){
		String v = String.valueOf(value);
		if(StringUtils.isNotBlank(fieldName) && StringUtils.isNotBlank(v)){
			add(Restrictions.like(fieldName,v));
		}
		return this;
	}
	/**
	 * 等于查询
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Criteria<T> addEq(String fieldName,Object value){
		if(StringUtils.isNotBlank(fieldName) && value!=null){
			add(Restrictions.eq(fieldName, value));
		}
		return this;
	}
	
	/**
	 * 不等于查询
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Criteria<T> addNeq(String fieldName,Object value){
		if(StringUtils.isNotBlank(fieldName) && value!=null){
			add(Restrictions.neq(fieldName, value));
		}
		return this;
	}
	
	/**
	 * 大于查询
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Criteria<T> addGt(String fieldName,Object value){
		if(StringUtils.isNotBlank(fieldName) && value!=null){
			add(Restrictions.gt(fieldName, value));
		}
		return this;
	}
	
	/**
	 * 大于等于查询
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Criteria<T> addGte(String fieldName,Object value){
		if(StringUtils.isNotBlank(fieldName) && value!=null){
			add(Restrictions.gte(fieldName, value));
		}
		return this;
	}
	
	/**
	 * 小于查询
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Criteria<T> addlt(String fieldName,Object value){
		if(StringUtils.isNotBlank(fieldName) && value!=null){
			add(Restrictions.lt(fieldName, value));
		}
		return this;
	}
	
	/**
	 * 小于等于查询
	 * @param fieldName
	 * @param value
	 * @return
	 */
	public Criteria<T> addlte(String fieldName,Object value){
		if(StringUtils.isNotBlank(fieldName) && value!=null){
			add(Restrictions.lte(fieldName, value));
		}
		return this;
	}
	
	@SuppressWarnings("rawtypes") 
	public Criteria<T> addIn(String fieldName,Collection value){
		if(StringUtils.isNotBlank(fieldName) && !CollectionUtils.isEmpty(value)){
			add(Restrictions.in(fieldName, value));
		}
		return this;
	}
	
	/**
	 * or查询
	 */
	/*public Criteria<T> addOr(QueryExpression expression){
		add(Restrictions.or(expression));
		return this;
	}*/
	
	public Criteria<T> addOr(LogicalExpression... expression){
		add(Restrictions.or(expression));
		return this;
	}
	
}